// APP入口, 设备相关基础数据, 设备相关基础函数
#include "stdbool.h"
// #include "time.h"

#include "zq_gl.h"
#include "zq_app.h"

#include "fota.h"
#include "wifiscan.h"
#include "btsinfo.h"
#include "dtu.h"
#include "mqtt.h"
#include "zq_ntp.h"
#include "self_test.h"

// ------------------------------------------------------------------------------------------------
ql_task_t zq_iot_task;
static ql_timer_t zq_iot_timer = 0;

char m0_sw_ver_str[ZQ_VER_STR_MAX_LEN];					// m0 software version string, 4位
char local_joint_ver_str[ZQ_VER_STR_MAX_LEN * 2];		// M0_Cat1联合版本string, 4位

uint32_t zq_timer_s = 0;							// 全局秒计时
static ZqLocalSaveData zq_local_saved;				// 本地要保存的数据
static DtuC01ResponsePkg zq_mcu_base_info = { 0 };	// M0基本数据
static int zq_first_query = 0;						// 上线发送基础信息后, 等待server query完成标志。
static ZqCircularFifo* zq_offline_fifo = NULL;		// 管理离线report数据

// static uint8_t zq_ble_uuid_def[] = {0xAB, 0x81, 0x90, 0xD5, 0xD1, 0x1E, 0x49, 0x41, 0xAC, 0xC4, 0x42, 0xF3, 0x05, 0x10, 0xB4, 0x08};
// static uint16_t zq_ble_major_def = 0x2773;
// static uint16_t zq_ble_minor_def = 0x89EF;

// IOT parameters buffers
static char zq_imei[IMEI_MAX] = { 0 };
static char zq_iccid[ICCID_MAX] = { 0 };
static char zq_imsi[IMSI_MAX] = { 0 };
static char zq_wifi_mac[24] = { 0 };
static char zq_bluetooth_mac[24] = { 0 };
static char zq_signal_intensity[8] = { 0 };
static char zq_ap_info[ZQ_AP_INFO_BUFLEN] = { 0 };
static char zq_bts_info[ZQ_BASE_SCAN_BUFLEN] = { 0 };
static char zq_near_bts_info[ZQ_BASE_SCAN_BUFLEN * ZQ_BASE_SCAN_NEAR_CNT] = { 0 };

#define IOT_TOPIC_BUFF_LEN 64
// subscribe topics
// subscribe topics: aucma/{Type}/{ID}/...
static char iot_topic_request[IOT_TOPIC_BUFF_LEN] = { 0 };
static char iot_topic_reply[IOT_TOPIC_BUFF_LEN] = { 0 };
// publish topics
// publish topics: aucma/{Type}/{ID}/...
static char iot_topic_up[IOT_TOPIC_BUFF_LEN] = { 0 };
static char iot_topic_response[IOT_TOPIC_BUFF_LEN] = { 0 };
static char iot_topic_quest[IOT_TOPIC_BUFF_LEN] = { 0 };

static ZqParams zq_pms = { 0 };
static KJ_METAS(ZqParams, (moduleId, KJ_PSTR), (controllerType, KJ_PSTR), (devType, KJ_PSTR), \
	(function, KJ_PSTR), (moduleType, KJ_PSTR), (softwareVers, KJ_PSTR), (hardwareVers, KJ_PSTR), \
	(accessType, KJ_INT), (manufactureCode, KJ_PSTR), (sim, KJ_PSTR), \
	(iccid, KJ_PSTR), (imsi, KJ_PSTR), (wifiMAC, KJ_PSTR), (bluetoothMAC, KJ_PSTR), \
	(highTemp, KJ_INT), (lowTemp, KJ_INT), (outageTemp, KJ_INT), (errorTemp, KJ_INT), \
	(upReturnTemp, KJ_INT_F1), (downReturnTemp, KJ_INT_F1), (sensorFaultCompressorTurnOnTime, KJ_INT), \
	(sensorFaultCompressorTurnOffTime, KJ_INT), (defrostInterval, KJ_INT), (defrostTime, KJ_INT), (minCompressorOffTime, KJ_INT), \
	(trueTemp, KJ_INT_F1), (temp, KJ_INT_F1), (doorStatus, KJ_INT), (outageState, KJ_INT), (localAction, KJ_INT), \
	(signalIntensity, KJ_PSTR), (ledCurrent, KJ_PSTR), (battery, KJ_INT), (power, KJ_INT), \
	(voltage, KJ_INT_F1), (btsInfo, KJ_PSTR), (nearsbtsNum, KJ_INT), (nearsbtsInfo, KJ_PSTR), (apNum, KJ_INT), \
	(apInfo, KJ_PSTR), (errorCode, KJ_PSTR), (humanTraffic, KJ_INT), (openCounts, KJ_INT), \
	(loadCurrent, KJ_INT_F1), (loadVoltage, KJ_INT), (activePower, KJ_INT_F1), \
	(tempSet, KJ_INT_F1), (tempCompensation, KJ_INT_F1), (tempHighLimit, KJ_INT_F1), \
	(tempLowLimit, KJ_INT_F1), (highTempAlarmValue, KJ_INT_F1), (lowTempAlarmValue, KJ_INT_F1), \
	(doorAngle, KJ_INT), (controllerState, KJ_INT), (uploadInterval, KJ_INT), \
	(lightState, KJ_INT), (fanState, KJ_INT), (doorAngle, KJ_INT), (version, KJ_INT), \
	(timestamp, KJ_INT), (tempControlMode, KJ_INT));

const char* zq_mqtt_up_info_strs[] = { "moduleId", "controllerType", "devType", "function", "moduleType", "softwareVers",
	"hardwareVers", "manufactureCode", "sim", "iccid", "imsi", "wifiMAC", "bluetoothMAC" };
static char* zq_mqtt_up_rpt_strs[32] = { "uploadInterval", "localAction", "signalIntensity", "power", "battery", "errorCode", "timestamp", "temp" };
static int zq_mqtt_up_rpt_num = 8;
static char* zq_mqtt_up_alarm_strs[4] = { "highTemp", "lowTemp", "errorTemp" };
static int zq_mqtt_up_alarm_num = 3;

// const char* zq_mqtt_up_update_strs[] = { "tempSet", "tempCompensation", "upReturnTemp", "downReturnTemp", "sensorFaultCompressorTurnOnTime",
// 	"sensorFaultCompressorTurnOffTime", "defrostInterval", "defrostTime", "tempHighLimit", "tempLowLimit", "highTempAlarmValue", "lowTempAlarmValue", "tempControlMode" };
const char* zq_mqtt_quest_shadow_strs[] = { "version", "timestamp" };

static uint8_t zq_error_code_flags[16] = { 0 };  // array to recode every error 
void ZqSetErrCode(ZQ_ERR_CODE code, uint8_t flag) {
	if (code == ZQ_ERR_CODE_NONE) // clear every error codes
		memset(zq_error_code_flags, 0, sizeof(zq_error_code_flags));
	else
		zq_error_code_flags[(int)code] = flag;
}
char* ZqGetErrCode(void) {
	static char zq_error_code_buf[28] = { 0 };
	char buf[4] = { 0 };
	//zq_error_code_buf[0] = 0;
	char* p = zq_error_code_buf;
	for (int i = 1; i < sizeof(zq_error_code_flags); i++) {
		if (zq_error_code_flags[i]) {
			sprintf(buf, "%X,", (0xe0 + i));
			strcpy(p, buf);
			p += strlen(buf);
		}
	}
	if (p == zq_error_code_buf) // not any error
		strcpy(p, "E0");
	else
		*(p - 1) = 0; // get rid of the last ","
	return zq_error_code_buf;
}

static void InitTopicString(void) {
	snprintf(iot_topic_request, sizeof(iot_topic_request), "aucma/%s/%s/request", zq_pms.devType, zq_imei);
	snprintf(iot_topic_reply, sizeof(iot_topic_reply), "aucma/%s/%s/reply", zq_pms.devType, zq_imei);
	snprintf(iot_topic_up, sizeof(iot_topic_up), "aucma/%s/%s/up", zq_pms.devType, zq_imei);
	snprintf(iot_topic_response, sizeof(iot_topic_response), "aucma/%s/%s/response", zq_pms.devType, zq_imei);
	snprintf(iot_topic_quest, sizeof(iot_topic_quest), "aucma/%s/%s/quest", zq_pms.devType, zq_imei);
}
// 生成cat1对外版本号
static void InitExternalVersion(void) {
	static char sw_ver_3digits[ZQ_VER_STR_MAX_LEN * 2];
	static char hw_ver_3digits[ZQ_VER_STR_MAX_LEN];
	int len = strlen(ZQ_SW_VER_STR) - 3;	// change from xxxx_V1.2.3.45 to xxxx_V1.2.3
	strncpy(sw_ver_3digits, ZQ_SW_VER_STR, len);
	sw_ver_3digits[len] = 0;
	len = strlen(m0_sw_ver_str) - 3;	// change from xxxx_V1.2.3.45 to xxxx_V1.2.3
	if (len >= 0) {
		strcat(sw_ver_3digits, "-");
		strncat(sw_ver_3digits, m0_sw_ver_str, len);
	}
	len = strlen(ZQ_HW_VER_STR) - 3;	// change from xxxx_V1.2.3.45 to xxxx_V1.2.3
	strncpy(hw_ver_3digits, ZQ_HW_VER_STR, len);
	zq_pms.softwareVers = sw_ver_3digits;
	zq_pms.hardwareVers = hw_ver_3digits;
}
ZqProductionInfo zq_production_info;
static void ZqLoadSim(void) {
	static char sim_num[32];
	zq_pms.sim = sim_num;
	// get sim and uuid from production NV
	if (zq_EasyFileRead(ZQ_UUID_FILE_PATH, &zq_production_info, sizeof(zq_production_info)) == sizeof(zq_production_info)) { // 如果不存在生成缺省的
		if (zq_production_info.sim_len != 0) {
			strcpy(zq_pms.sim, (char*)zq_production_info.sim_num);
			return;
		}
	}
	strcpy(zq_pms.sim, zq_imei + 2); // 根据imei生成模拟sim
	return;
}
static void ZqInitParamsFromM0(void) {
	dtu_cmd01(&zq_mcu_base_info);
	if (zq_mcu_base_info.com_protocol_ver != COMM_PROTOCOL_VER) { // cmd01通讯失败或者通讯协议版本不对, 启动为独立定位模块 - TODO or reset？
		strcpy(m0_sw_ver_str, "AAQM_V0.0.0.00");	// 906-独立定位模块
		ZQLOG("Cmd01 error or protocol version error!, will reset after 5*60 sec!!!");
		ql_rtos_task_sleep_s(5 * 60);
		ql_power_reset(RESET_NORMAL);
	}
	sprintf(m0_sw_ver_str, "%sM_V%s", zq_mcu_base_info.zq_ver_prefix, zq_mcu_base_info.m0_sw_ver);
	//selftest_report_p->m0_start_cause = zq_mcu_base_info.m0_start_cause;
	//selftest_report_p->cat1_start_cause = zq_mcu_base_info.cat1_start_cause;
	zq_pms.controllerType = zq_mcu_base_info.akm_ctrl_type;
	zq_pms.devType = zq_mcu_base_info.akm_dev_type;
	zq_pms.moduleType = zq_mcu_base_info.akm_module_type;
	// zq_pms.function = zq_mcu_base_info.akm_ctrl_func;
	static char function_buf[64];  // max support 21 functions
	for (int x = 1; x < 32; x++) {
		if ((zq_mcu_base_info.akm_ctrl_func & (1 << x)) == 0) continue; // 0 means not support
		char buf[4];
		sprintf(buf, "%02d,", x);
		strcat(function_buf, buf);

		if (x == FUN_WIFI) {
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "btsInfo";
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "nearsbtsNum";
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "nearsbtsInfo";
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "apNum";
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "apInfo";
		} else if (x == FUN_BT) {
			// get ble mac
			sprintf(zq_pms.bluetoothMAC, "%02X:%02X:%02X:%02X:%02X:%02X", zq_mcu_base_info.ble_mac[5], zq_mcu_base_info.ble_mac[4], zq_mcu_base_info.ble_mac[3], zq_mcu_base_info.ble_mac[2], zq_mcu_base_info.ble_mac[1], zq_mcu_base_info.ble_mac[0]);
			ZQLOG("ZqBleGetMac: %s", zq_pms.bluetoothMAC);
		}
		else if (x == FUN_TCM) {
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "trueTemp";
		} else if (x == FUN_COMPRESS) {
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "controllerState";
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "outageState";
		} else if (x == FUN_DOORSW) {
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "doorStatus";
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "openCounts";
		} else if (x == FUN_BATTERY) {
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "voltage";
			zq_mqtt_up_alarm_strs[zq_mqtt_up_alarm_num++] = "outageTemp";
		} else if (x == FUN_HUMMANCHK) {
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "humanTraffic";
		} else if (x == FUN_LIGHTCTRL) {
			zq_mqtt_up_rpt_strs[zq_mqtt_up_rpt_num++] = "lightState";
		}
	}
	function_buf[strlen(function_buf) - 1] = 0; // remove the last comma
	zq_pms.function = function_buf;
	// Kg modified 20231222， 客人要求分体显示的上报显示温度不带小数, 要修改(temp, KJ_INT_F1) --> (temp, KJ_INT)
	// fix temperature unit by display pannel
	if (zq_mcu_base_info.display_panel_type == DPT_SPLIT_1 || zq_mcu_base_info.display_panel_type == DPT_SPLIT_2 || zq_mcu_base_info.display_panel_type == DPT_SPLIT_1x) {
		meta_t* pmeta_tem = ZqMetasFind(ZqParams_meta, "temp");
		short* tp = (short*)pmeta_tem;
		*tp = KJ_INT_1;
	}
}
void ZqInitParams(void) {
	// 读取sim related info
	ql_dev_get_imei(zq_imei, sizeof(zq_imei), ZQ_SIM_0);
	// 读imsi会和modem通信, 可能出错, 尝试多次
	for (int i = 0; (i < 100) && (ql_sim_get_imsi(ZQ_SIM_0, zq_imsi, sizeof(zq_imsi)) != QL_SIM_SUCCESS); i++, ql_rtos_task_sleep_ms(200)); // add a little delay, or it seems that we continuous operate sim functions will get an error.
	// 读iccid会和modem通信, 可能出错, 尝试多次
	for (int i = 0; (i < 100) && (ql_sim_get_iccid(ZQ_SIM_0, zq_iccid, sizeof(zq_iccid)) != QL_SIM_SUCCESS); i++, ql_rtos_task_sleep_ms(200)); // add a little delay, or it seems that we continuous operate sim functions will get an error.
	ZQLOG("get: imsi: %s, iccid: %s", zq_imsi, zq_iccid);
	// 获取wifi mac info
	ZQ_IMEI2MAC(zq_wifi_mac, zq_imei);

	if (*zq_imei == 0 || *zq_imsi == 0)		// imei or imsi get error
		ZqSetErrCode(ZQ_ERR_CODE_NO_SIM, 1);
	else
		ZqSetErrCode(ZQ_ERR_CODE_NO_SIM, 0);

	// 读取存储的配置信息.
	zq_EasyMkDir(ZQ_CONFIG_DIR_PATH);
	int ret = zq_EasyFileRead(ZQ_CONFIG_FILE_PATH, &zq_local_saved, sizeof(zq_local_saved));
	if (ret != sizeof(zq_local_saved) || zq_local_saved.uploadInterval < 10) {
		// 读取文件失败 or 参数错误, 重新初始化创建
		zq_local_saved.uploadInterval = 30;
		zq_local_saved.version = 0;
		zq_EasyFileWrite(ZQ_CONFIG_FILE_PATH, &zq_local_saved, sizeof(zq_local_saved));
	}

	zq_pms.moduleId = zq_imei;
	zq_pms.manufactureCode = CFG_MANUFACTURE_CODE;

	zq_pms.accessType = 0;		// 接入网络方式  查询 int 0:移动网络，1:WIFI
	zq_pms.imsi = zq_imsi;
	zq_pms.iccid = zq_iccid;
	zq_pms.wifiMAC = zq_wifi_mac;
	zq_pms.bluetoothMAC = zq_bluetooth_mac;

	zq_pms.power = 0;
	zq_pms.battery = 0;
	zq_pms.timestamp = 0; 		// 设置为0，表示akm_params第一次初始化。
	// 3. set some dynamic iot params buffer. 
	zq_pms.signalIntensity = zq_signal_intensity;
	zq_pms.apInfo = zq_ap_info;
	zq_pms.errorCode = ZqGetErrCode();
	zq_pms.btsInfo = zq_bts_info;
	zq_pms.nearsbtsInfo = zq_near_bts_info;
	// 4. 设置存储的配置信息.
	zq_pms.uploadInterval = zq_local_saved.uploadInterval;
	zq_pms.version = zq_local_saved.version;	// 影子版本
	// 5. get mcu version info and ble mac
	zq_pms.errorTemp = 0;	// 传感器报警清零
	zq_pms.bluetoothMAC[0] = 0;
	//InitExternalVersion();
	ZqLoadSim();
	// 初始化m0相关数据
	ZqInitParamsFromM0();
	// 获取cat1对外版本号
	InitExternalVersion();

#if (!ZQ_SIMULATOR)
	// [panzhimin] ftm code (Cat1跟随M0的FTM模式)
	if (zq_mcu_base_info.bootmode == 1 || zq_mcu_base_info.bootmode == 2) {
		ZQLOG("[FTM]: Change Work mode to %s", zq_mcu_base_info.bootmode == 1 ? "SA" : "FA");
		extern void ftm_bootmode_write(uint8_t bootmode, bool reboot);
		ftm_bootmode_write(zq_mcu_base_info.bootmode, true);
	}
	// [panzhimin] ftm code end
#endif

	zq_pms.controllerState = 1;
	zq_pms.localAction = 1;
	zq_pms.outageState = 1;

	// init offline data fifo 
	zq_offline_fifo = ZqFifoCreate(offline_msgs_path, ZQ_OFFLINE_MSG_MAX_CNT);
	//zq_StackCreate(&zq_offline_msgs, ZQ_OFFLINE_MSG_MAX_CNT);
}

// if (A != B) 把B(byte/char, 0x7f为非法值)合并到A，并记录下名字; return: 1 - 合并, 0 - 没合并
static int MergeByteAB(int* A, char B, char* name, char** rec) {
	if (*A != (int)B) {
		if (IsByteValid(B)) {
			*rec = name;
			*A = (int)B;
			return 1;
		} else
			*A = NUM_INVALID_DWORD;
	}
	return 0;
}
// if (A != B) 把B(word/short, 0x7fff为非法值)合并到A，并记录下名字; return: 1 - 合并, 0 - 没合并
static int MergeWordAB(int* A, short B, char* name, char** rec) {
	if (*A != (int)B) {
		if (IsWordValid(B)) {
			*rec = name;
			*A = (int)B;
			return 1;
		} else
			*A = NUM_INVALID_DWORD;
	}
	return 0;
}

// 每个上报周期，统计本周期内的数值。
static void ReportPeriodStatistics(void) {
	static int last_openCounts = 0;			// 门开计次, 107
	static int last_humanTraffic = 0;			// 人流计次, 107
	//static int last_112 = 0;			// 压机开机计次, 107
	int tmp;
	tmp = zq_pms.openCounts - last_openCounts;
	last_openCounts = zq_pms.openCounts;
	if (zq_timer_s > 40 * 60) // akm系列要求: 前40分钟不清零
		zq_pms.openCounts = tmp;

	tmp = zq_pms.humanTraffic - last_humanTraffic;
	last_humanTraffic = zq_pms.humanTraffic;
	if (zq_timer_s > 40 * 60) // akm系列要求: 前40分钟不清零
		zq_pms.humanTraffic = tmp;
	//tmp = zq_pms.k_112 - last_112;
	//last_112 = zq_pms.k_112;
	//zq_pms.k_112 = tmp;
}

// 把dtu cmd02 抓出的数据合并到IOT全局变量
static void IotMergeDtuData(DtuC02ResponsePkg* recv, char** updates, int* update_cnt, char** alarms, int* alarm_cnt) {
	*update_cnt = *alarm_cnt = 0;

	// if (zq_pms.tempControlMode != recv->ctrl_mode) force = true;  // TODO 冷热模式变更，强制update全部参数，不管实际值是否变化。
	*update_cnt += MergeWordAB(&zq_pms.tempSet, recv->TS, "tempSet", updates + *update_cnt);
	*update_cnt += MergeWordAB(&zq_pms.tempCompensation, recv->C5, "tempCompensation", updates + *update_cnt);
	*update_cnt += MergeWordAB(&zq_pms.upReturnTemp, recv->C1, "upReturnTemp", updates + *update_cnt);
	*update_cnt += MergeWordAB(&zq_pms.downReturnTemp, recv->C2, "downReturnTemp", updates + *update_cnt);
	*update_cnt += MergeWordAB(&zq_pms.sensorFaultCompressorTurnOnTime, recv->C3, "sensorFaultCompressorTurnOnTime", updates + *update_cnt);
	*update_cnt += MergeWordAB(&zq_pms.sensorFaultCompressorTurnOffTime, recv->C4, "sensorFaultCompressorTurnOffTime", updates + *update_cnt);
	*update_cnt += MergeWordAB(&zq_pms.minCompressorOffTime, recv->C7, "minCompressorOffTime", updates + *update_cnt);
	*update_cnt += MergeWordAB(&zq_pms.defrostInterval, recv->d1, "defrostInterval", updates + *update_cnt);
	*update_cnt += MergeWordAB(&zq_pms.defrostTime, recv->d2, "defrostTime", updates + *update_cnt);
	*update_cnt += MergeWordAB(&zq_pms.tempHighLimit, recv->r2, "tempHighLimit", updates + *update_cnt);
	*update_cnt += MergeWordAB(&zq_pms.tempLowLimit, recv->r1, "tempLowLimit", updates + *update_cnt);
	*update_cnt += MergeWordAB(&zq_pms.highTempAlarmValue, recv->AH, "highTempAlarmValue", updates + *update_cnt);
	*update_cnt += MergeWordAB(&zq_pms.lowTempAlarmValue, recv->AL, "lowTempAlarmValue", updates + *update_cnt);
	if (recv->ctrl_mode & CTRL_MODE_HOT) // TODO, 暂不考虑ECO模式
		*update_cnt += MergeByteAB(&zq_pms.tempControlMode, 1, "tempControlMode", updates + *update_cnt);
	else
		*update_cnt += MergeByteAB(&zq_pms.tempControlMode, 0, "tempControlMode", updates + *update_cnt);
	*update_cnt += MergeByteAB(&zq_pms.lightState, recv->light_state, "lightState", updates + *update_cnt);

	// 一共四个报警, 高低温报警
	// force = false;
	*alarm_cnt += MergeByteAB(&zq_pms.highTemp, ((recv->alarm & ZQ_ALARM_H) ? 1 : 0), "highTemp", alarms + *alarm_cnt);
	*alarm_cnt += MergeByteAB(&zq_pms.lowTemp, ((recv->alarm & ZQ_ALARM_L) ? 1 : 0), "lowTemp", alarms + *alarm_cnt);
	// 传感器故障
	*alarm_cnt += MergeByteAB(&zq_pms.errorTemp, ((recv->alarm & ZQ_ALARM_SENSOR) ? 1 : 0), "errorTemp", alarms + *alarm_cnt);
	//if (!(recv->alarm & ZQ_ALARM_SENSOR)) {
	if (recv->alarm & ZQ_ALARM_SENSOR) {
		if (recv->ctrl_mode == CTRL_MODE_FREEZE)
			ZqSetErrCode(ZQ_ERR_CODE_FREEZE, 1);
		else
			ZqSetErrCode(ZQ_ERR_CODE_COOL, 1);
	} else {
		ZqSetErrCode(ZQ_ERR_CODE_COOL, 0);
		ZqSetErrCode(ZQ_ERR_CODE_FREEZE, 0);
	}
	//}
	// 断电
	*alarm_cnt += MergeByteAB(&zq_pms.outageTemp, ((recv->alarm & ZQ_ALARM_POW) ? 1 : 0), "outageTemp", alarms + *alarm_cnt);

	// 其他周期上报参数
	zq_pms.power = (recv->alarm & ZQ_ALARM_POW) ? 1 : 0;  // 市电供电0，电池供电1
	zq_pms.battery = (IsWordValid(recv->vbat)) ? 1 : 0;
	zq_pms.voltage = (IsWordValid(recv->vbat)) ? ((recv->vbat + 50) / 100) : NUM_INVALID_DWORD;
	zq_pms.outageState = (recv->comp_state == COMP_STATE_RUN) ? 1 : 0; 						// recv->comp_state 并不是压机真正启停状态
	zq_pms.controllerState = ((recv->ctrl_switch & CTRL_REMOTE_SWITCH) == CTRL_REMOTE_SWITCH) ? 1 : 0;
	zq_pms.localAction = ((recv->ctrl_switch & CTRL_HARD_SWITCH) && (recv->ctrl_switch & CTRL_SOFT_SWITCH));
	zq_pms.trueTemp = (IsTemValid(recv->m_temp)) ? recv->m_temp : 0;
#if (ZQ_DISPLAY_PANEL_TYPE == 1) // Kg modified 20231222， 客人要求分体显示的上报显示温度不带小数
	zq_pms.temp = (IsTemValid(recv->d_temp)) ? recv->d_temp : 0;
#else
	zq_pms.temp = (IsTemValid(recv->d_temp)) ? recv->d_temp : 0;
#endif
	zq_pms.openCounts = recv->door1_count;
	zq_pms.humanTraffic = recv->humman;
	zq_pms.doorStatus = (recv->door1_timer > 0) ? 1 : 0;
	zq_pms.loadVoltage = recv->load_vol;
	zq_pms.loadCurrent = recv->load_cur;
	zq_pms.activePower = recv->load_pow * 10;
	// int8_t AD; //报警延时
	// pDev->C6 化霜显示解锁时间
	return;
}

// ----------------------------------------
static int IotPublishMsg(const char* pTopic, char* cmd, void* pData, const meta_t* pMetas, int qos) {
	cJSON* root = cJSON_CreateObject();
	if (root == NULL) goto IotPublishMsg_err;
	cJSON* params = NULL;
	if (!cJSON_AddItemToObject(root, "cmd", cJSON_CreateString(cmd))) goto IotPublishMsg_err;
	char* payload = NULL;
	int ret = 0;

	if (pMetas != NULL) {  // 有的cmd没有payload，ex，mqtt protocol 3.12 查询时间戳
		params = cJSON_CreateObject();
		if (params == NULL) goto IotPublishMsg_err;
		ret = KgStruct2Json(params, pData, pMetas);
		if (ret != 0) goto IotPublishMsg_err;
		if (!cJSON_AddItemToObject(root, "params", params)) goto IotPublishMsg_err;
	}
	payload = cJSON_PrintUnformatted(root);
	if (payload == NULL) goto IotPublishMsg_err;
	// ZQLOG("Payload: %s", payload);
	ret = mqtt_publish_msg(pTopic, payload, qos);
	cJSON_Delete(root);
	free(payload);
	return ret;

IotPublishMsg_err:
	//if (params) cJSON_Delete(params);
	if (root) cJSON_Delete(root);
	return ZQ_RET_ERR;
}
static int IotPublishMsgReportToBuffer(void* pData, const meta_t* pMetas) {
	cJSON* root = cJSON_CreateObject();
	char* payload = NULL;
	if (root == NULL) goto IotPublishMsgReportToBuffer_err;
	//cJSON* data = NULL;
	int ret = KgStruct2Json(root, pData, pMetas);
	if (ret != 0) goto IotPublishMsgReportToBuffer_err;
	payload = cJSON_PrintUnformatted(root);
	if (payload == NULL) goto IotPublishMsgReportToBuffer_err;

	char buf[ZQ_OFFLINE_MSG_SIZE];
	strncpy(buf, payload, ZQ_OFFLINE_MSG_SIZE);
	strcat(buf, ",");
	free(payload);
	//zq_StackPush(zq_offline_msgs, payload);
	ret = ZqFifoAdd(zq_offline_fifo, buf);
	ZQLOG("Mqtt Protocol 3.4 offline - save to offline file, msg no. = %d, timestamp = %d", ret, zq_pms.timestamp);
	cJSON_Delete(root);
	return ret;

IotPublishMsgReportToBuffer_err:
	if (root) cJSON_Delete(root);
	return ZQ_RET_ERR;
}


// Mqtt Protocol 3.3, device register 
static meta_t* zq_mqtt_up_info_metas = NULL;
int MqttPublishUpInfo(void) {
	ZQLOG("Mqtt Protocol 3.3 - Publish up-info");
	if (zq_mqtt_up_info_metas == NULL)
		zq_mqtt_up_info_metas = ZqMetasTrim(ZqParams_meta, sizeof(zq_mqtt_up_info_strs) / sizeof(zq_mqtt_up_info_strs[0]), zq_mqtt_up_info_strs);
	if (IotPublishMsg(iot_topic_up, "info", &zq_pms, zq_mqtt_up_info_metas, 1) != ZQ_RET_OK) {
		ZQLOG("@err@: Publish up-info error.");
		return ZQ_RET_ERR;
	}
	return ZQ_RET_OK;
}
// Mqtt Protocol 3.4 周期上报信息, 
static meta_t* zq_mqtt_up_rpt_metas = NULL;
int MqttPublishUpReport(void) {
	ZQLOG("Mqtt Protocol 3.4 - Publish up-report");
	zq_pms.timestamp = ZqGetUTCStample();
	ReportPeriodStatistics();				// 收集周期数据
	if (zq_mqtt_up_rpt_metas == NULL)
		zq_mqtt_up_rpt_metas = ZqMetasTrim(ZqParams_meta, zq_mqtt_up_rpt_num, (const char**)zq_mqtt_up_rpt_strs);
	// 收集错误代码
	zq_pms.errorCode = ZqGetErrCode();
	if (ZyIsMqttConnected()) { // 在线发送，不在线缓存
		if (IotPublishMsg(iot_topic_up, "report", &zq_pms, zq_mqtt_up_rpt_metas, 1) != ZQ_RET_OK) {
			ZQLOG("@err@: Publish up-report error.");
			return ZQ_RET_ERR;
		}
	} else {
		IotPublishMsgReportToBuffer(&zq_pms, zq_mqtt_up_rpt_metas);
	}

	// clear some accumulation value
	if (zq_timer_s >= 40 * 60)		// akm spec 6.3.17 模块通电前 40 分钟，柜门开关次数累加不清零
		zq_pms.openCounts = 0;
	zq_pms.humanTraffic = 0;
	return ZQ_RET_OK;
}
// Mqtt Protocol 3.5, meta_strs => 报警的item key string
int MqttPublishUpAlarm(char** alarms, int alarm_cnt) {
	ZQLOG("Mqtt Protocol 3.5 - Publish up-alarm");
	zq_pms.timestamp = ZqGetUTCStample();
	char* single_alarm[] = { "timestamp", "temp", NULL };
	// aucma 要求所有的alarm单独上报
	for (int i = 0; i < alarm_cnt; i++) {
		single_alarm[2] = alarms[i];
		meta_t* metas = ZqMetasTrim(ZqParams_meta, 3, (const char**)single_alarm);
		if (IotPublishMsg(iot_topic_up, "alarm", &zq_pms, metas, 1) != ZQ_RET_OK) {
			ZQLOG("@err@: Publish up-report error.");
			free(metas);
			return ZQ_RET_ERR;
		}
		free(metas);
	}
	return ZQ_RET_OK;
}
//typedef struct {
//	int cnt;
//	char* params[64];
//} IotQueryArray;

// static KJ_META_ARRAY(IotQueryArray, KJ_ARRAY, KJ_PSTR);
// Mqtt Protocol 3.6, 应答查询信息请求
int MqttResponseQuery(cJSON* root) {
	ZQLOG("Mqtt Protocol 3.6 - Publish response-set");
	char* list[64];
	KJList query = { .cnt = 64, list };
	meta_t tp = KJ_PSTR;

	zq_first_query = 1;
	cJSON* param = cJSON_GetObjectItem(root, "params");
	if (param == NULL) return ZQ_RET_ERR;
	KgJson2List(param, &query, &tp);

	meta_t* metas = ZqMetasTrim(ZqParams_meta, query.cnt, (const char**)(query.list));
	if (IotPublishMsg(iot_topic_response, "query", &zq_pms, metas, 1) != ZQ_RET_OK) {
		ZQLOG("@err@: Publish quest-shadow error.");
		free(metas);
		return ZQ_RET_ERR;
	}
	free(metas);
	return ZQ_RET_OK;
}

typedef struct IOT_RemoteSet {
	int tempSet;
	int tempCompensation;
	int tempHighLimit;
	int tempLowLimit;
	int highTempAlarmValue;
	int lowTempAlarmValue;
	int controllerState;
	int uploadInterval;
	int tempControlMode;
	int lightState;
} IotRequestSet;
static KJ_METAS(IotRequestSet, (tempSet, KJ_INT_F1), (tempCompensation, KJ_INT_F1), (tempHighLimit, KJ_INT_F1), (tempLowLimit, KJ_INT_F1), \
	(highTempAlarmValue, KJ_INT_F1), (lowTempAlarmValue, KJ_INT_F1), (controllerState, KJ_INT), (uploadInterval, KJ_INT), (tempControlMode, KJ_INT), (lightState, KJ_INT));
static void IotRemoteSet(IotRequestSet* sets) {
	ZQLOG("Mqtt Get remote set: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",
		sets->tempSet, sets->tempCompensation, sets->tempHighLimit, sets->tempLowLimit, sets->highTempAlarmValue, sets->lowTempAlarmValue, sets->controllerState, sets->uploadInterval, sets->tempControlMode, sets->lightState);
	// remote set uploadInterval 
	if (IsDWORDValid(sets->uploadInterval) && (sets->uploadInterval != zq_pms.uploadInterval)) {
		zq_local_saved.uploadInterval = zq_pms.uploadInterval = sets->uploadInterval;
		zq_EasyFileWrite(ZQ_CONFIG_FILE_PATH, &zq_local_saved, sizeof(zq_local_saved));
	}
#if (CFG_DEVICE_TYPE == 906)	// akm906 has not MCU
	return;
#endif
	// 设置下发mcu数据
	int chg = 0;
	DtuC03ReqRespPkg cmd03 = { NUM_INVALID_BYTE, NUM_INVALID_BYTE, NUM_INVALID_BYTE, NUM_INVALID_BYTE, NUM_INVALID_WORD, NUM_INVALID_WORD, NUM_INVALID_WORD, NUM_INVALID_WORD, NUM_INVALID_WORD, NUM_INVALID_WORD };
	if (sets->tempControlMode >= 0 && sets->tempControlMode < 2 && sets->tempControlMode != zq_pms.tempControlMode) { 	// special set command: remote switch cool/hot mode
		// 冷热柜特有的模式切换, cat1会下发两次cmd03，间隔100ms，第一次ctrl_mode=0/1，通知冷热切换，第二次ctrl_mode=0x80通知上报切换后的所有变更参数
		cmd03.ctrl_mode = (sets->tempControlMode == 1) ? CTRL_MODE_HOT : CTRL_MODE_COOL;
		dtu_cmd03(&cmd03);
		ql_rtos_task_sleep_ms(100); // wait mcu switch mode
		cmd03.ctrl_mode = 0x80;
		dtu_cmd03(&cmd03);
	} else { // normal set command
		// remote  tunr on/off controller
		if (sets->controllerState == 0 || sets->controllerState == 1) { cmd03.ctrl_remote_switch = sets->controllerState; chg++; }
		if (IsDWORDValid(sets->tempSet)) { cmd03.pm_TS = (short)sets->tempSet; chg++; }
		if (IsDWORDValid(sets->tempLowLimit)) { cmd03.pm_r1 = (short)sets->tempLowLimit; chg++; }
		if (IsDWORDValid(sets->tempHighLimit)) { cmd03.pm_r2 = (short)sets->tempHighLimit; chg++; }
		if (IsDWORDValid(sets->tempCompensation)) { cmd03.pm_C5 = (short)sets->tempCompensation; chg++; }
		if (IsDWORDValid(sets->highTempAlarmValue)) { cmd03.pm_AH = (short)sets->highTempAlarmValue; chg++; }
		if (IsDWORDValid(sets->lowTempAlarmValue)) { cmd03.pm_AL = (short)sets->lowTempAlarmValue; chg++; }
		if (sets->lightState == 0 || sets->lightState == 1) { cmd03.light_state = sets->lightState; chg++;}
		if (chg > 0) // 有任何有效数据
			dtu_cmd03(&cmd03);
	}
	// 回收mcu变更
	if (IsDWORDValid(sets->tempSet))
		zq_pms.tempSet = sets->tempSet = cmd03.pm_TS;
	if (IsDWORDValid(sets->tempLowLimit))
		zq_pms.tempLowLimit = sets->tempLowLimit = cmd03.pm_r1;
	if (IsDWORDValid(sets->tempHighLimit))
		zq_pms.tempHighLimit = sets->tempHighLimit = cmd03.pm_r2;
	if (IsDWORDValid(sets->tempCompensation))
		zq_pms.tempCompensation = sets->tempCompensation = cmd03.pm_C5;
	if (IsDWORDValid(sets->highTempAlarmValue))
		zq_pms.highTempAlarmValue = sets->highTempAlarmValue = cmd03.pm_AH;
	if (IsDWORDValid(sets->lowTempAlarmValue))
		zq_pms.lowTempAlarmValue = sets->lowTempAlarmValue = cmd03.pm_AL;
	if (IsDWORDValid(sets->tempControlMode) && IsByteValid(cmd03.ctrl_mode))
		zq_pms.tempControlMode = sets->tempControlMode = (cmd03.ctrl_mode & CTRL_MODE_HOT) ? 1 : 0; // TODO, 暂不考虑ECO模式
	if (IsDWORDValid(sets->controllerState))
		zq_pms.controllerState = sets->controllerState = cmd03.ctrl_remote_switch;
	ZQLOG("Mqtt set data: %d, %d, %d, %d, %d, %d, %d, %d, %d",
		sets->tempSet, sets->tempCompensation, sets->tempHighLimit, sets->tempLowLimit, sets->highTempAlarmValue, sets->lowTempAlarmValue, sets->controllerState, sets->uploadInterval, sets->tempControlMode);
}
static void MqttResponseSet(cJSON* root) {
	ZQLOG("Mqtt Protocol 3.7 - Publish response-set");
	cJSON* param = cJSON_GetObjectItem(root, "params");
	if (param == NULL) return;
	IotRequestSet sets;
	KgJson2Struct(param, &sets, IotRequestSet_meta);
	IotRemoteSet(&sets);
	// response server set command
	if (IotPublishMsg(iot_topic_response, "set", &sets, IotRequestSet_meta, 1) != ZQ_RET_OK)
		ZQLOG("@err@: Publish response-set error.");
	return;
}
// protocol 3.8
static meta_t* zq_mqtt_quest_shadow_metas = NULL;
static int MqttPublishQuestShadow(void) {
	ZQLOG("Mqtt Protocol 3.8 - Publish quest-shadow");
	zq_pms.timestamp = ZqGetUTCStample();
	if (zq_mqtt_quest_shadow_metas == NULL)
		zq_mqtt_quest_shadow_metas = ZqMetasTrim(ZqParams_meta, sizeof(zq_mqtt_quest_shadow_strs) / sizeof(zq_mqtt_quest_shadow_strs[0]), zq_mqtt_quest_shadow_strs);
	if (IotPublishMsg(iot_topic_quest, "shadow", &zq_pms, zq_mqtt_quest_shadow_metas, 1) != ZQ_RET_OK) {
		ZQLOG("@err@: Publish quest-shadow error.");
		return ZQ_RET_ERR;
	}
	return ZQ_RET_OK;
}
typedef struct {
	int version;
	int timestamp;
	IotRequestSet desired;
} IotShadowInfo;
static KJ_METAS(IotShadowInfo, (version, KJ_INT), (timestamp, KJ_INT), (desired, KJ_OBJ, IotRequestSet_meta));
// protocol 3.9 - 3.10
void MqttReplyShadow(cJSON* root) {
	ZQLOG("Mqtt Protocol 3.9-3.10 - Publish response-shadow");
	cJSON* param = cJSON_GetObjectItem(root, "params");
	if (param == NULL) return;
	IotShadowInfo shadow;
	IotRequestSet* pdesired = &shadow.desired;
	KgJson2Struct(param, &shadow, IotShadowInfo_meta);
	if (!IsDWORDValid(shadow.version) || shadow.version <= 0) return;   // 测试发现平台可能会回复param为空的无效影子，这是可以根据是否有有效版本号判断
	// Modified by Kaygle -- 2024-01-15 -- QA/RD协商, 影子下发就同步(不再考虑version是否一致)。
	IotRemoteSet(pdesired);
	if (zq_pms.version != shadow.version) {
		zq_local_saved.version = zq_pms.version = shadow.version;
		zq_EasyFileWrite(ZQ_CONFIG_FILE_PATH, &zq_local_saved, sizeof(zq_local_saved));
	}
	// 3.10 设备根据设备影子信息的状态“更改设备真实属性状态后”，上传信息给平台 -- 认为更改才需要response -- kg modify 20231025, 客人QA要求无论是否修改，都response
	shadow.timestamp = ZqGetUTCStample();
	// response server set command
	if (IotPublishMsg(iot_topic_response, "shadow", &shadow, IotShadowInfo_meta, 1) != ZQ_RET_OK)
		ZQLOG("@err@: Publish response-shadow error.");
	return;
}
// Protocol 3.11, updates => 上传的item key string
int MqttPublishUpUpdate(char** updates, int update_cnt) {
	// int ret = 0;
	if (update_cnt > 14) return -1; // 根据协议3.11, update最多14个item
	meta_t* metas = ZqMetasTrim(ZqParams_meta, update_cnt, (const char**)updates);
	if (IotPublishMsg(iot_topic_up, "update", &zq_pms, metas, 1) != ZQ_RET_OK) {
		ZQLOG("@err@: Publish up-update error.");
		free(metas);
		return ZQ_RET_ERR;
	}
	free(metas);
	return ZQ_RET_OK;
}
// Mqtt Protocol 3.12, quest timestamp
static int MqttPublishQuestTimestamp(void) {
	ZQLOG("Mqtt Protocol 3.12 - Publish quest-time");
	if (IotPublishMsg(iot_topic_quest, "timestamp", &zq_pms, NULL, 1) != ZQ_RET_OK) {
		ZQLOG("@err@: Publish quest-timestamp error.");
		return ZQ_RET_ERR;
	}
	return ZQ_RET_OK;
}
// protocol 3.12
#include <time.h>
void MqttReplyTimestamp(cJSON* root) {
	ZQLOG("Mqtt Protocol 3.12 - Receive response-timespan");
	cJSON* param = cJSON_GetObjectItem(root, "params");
	if (param == NULL) return;
	cJSON* timestamp = cJSON_GetObjectItem(param, "timestamp");
	if (timestamp == NULL) return;
	time_t t = (time_t)(timestamp->valueint);
	ql_rtc_time_t* tm = (ql_rtc_time_t*)localtime(&t); // 注意: ql平台的localtime会剥离时区，返回格林威治时间
	tm->tm_year += 1900;
	tm->tm_mon += 1;
	if (tm->tm_year <= 2023) return;
	if (ql_rtc_set_time(tm) == QL_RTC_SUCCESS) {
		char time_str[256] = { 0 };
		snprintf(time_str, 256, "%04d/%02d/%02d,%02d:%02d:%02d", tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
		ZQLOG("mqtt sync time:%s", time_str);
	}
	return;
}

// Protocol 3.13, report offline
int MqttPublishUpReportOffline(void) {
	int msg_cnt = ZqFifoGetMsgCnt(zq_offline_fifo);
	if(msg_cnt == 0) return msg_cnt;

	int buf_size = ZQ_OFFLINE_MSG_SIZE * ZQ_OFFLINE_SEND_CNT;
	char* prefix = "{\"cmd\":\"offline\",\"params\":{\"data\":[";
	char* suffix = "]}}";
	int step = strlen(prefix);
	char* buf = malloc(buf_size + step + strlen(suffix));
	if (buf == NULL) return 0;
	strcpy(buf, prefix);
	//char* payload = zq_StackPop(zq_offline_msgs);
	msg_cnt = ZqFifoGet(zq_offline_fifo, buf + step, buf_size, ZQ_OFFLINE_SEND_CNT);
	int len = strlen(buf);
	buf[len - 1] = 0;  // remove the last ","
	strcpy(buf + len - 1, suffix);
	int ret = mqtt_publish_msg(iot_topic_up, buf, 1);
	free(buf);
	if (ret == ZQ_RET_OK) 
		ZqFifoRemove(zq_offline_fifo, ZQ_OFFLINE_SEND_CNT);
	return msg_cnt; // TODO 没有重试机制，不管发出去没有，都返回发送的msg数量
}
static void MqttInpubRequest(cJSON* root) {
	cJSON* cmd = cJSON_GetObjectItem(root, "cmd");
	if (cmd == NULL) return;
	if (strcmp((const char*)cmd->valuestring, "set") == 0)
		MqttResponseSet(root);
	else if (strcmp((const char*)cmd->valuestring, "query") == 0)
		MqttResponseQuery(root);
	return;
}
static void MqttInpubReply(cJSON* root) {
	cJSON* cmd = cJSON_GetObjectItem(root, "cmd");
	if (cmd == NULL) return;

	if (strcmp(cmd->valuestring, "shadow") == 0)
		MqttReplyShadow(root);
	else if (strcmp(cmd->valuestring, "timestamp") == 0)
		ZQLOG("mqtt_inpub_reply timestamp");  // TODO 同步时间
	return;
}

static int IotQueryMcuData(DtuC02ResponsePkg* recv) {
	int ret = 0;
	// build the cmd02 send data
	DtuC02RequestPkg cmd_pkg;
	//cmd_pkg.clear = clear;
	if (ZyIsMqttConnected()) // cmd_pkg._4g = 0 - 拨号失败; 1 -- 拨号成功; 2 -- 连接上平台
		cmd_pkg._4g = MODE_4G_CONNECT;
	else if (ql_datacall_get_sim_profile_is_active(ZQ_SIM_0, ZQ_PROFILE_IDX) == 1)
		cmd_pkg._4g = MODE_4G_DIAL;
	else
		cmd_pkg._4g = MODE_4G_NONE;
	cmd_pkg.wifi = (zq_GetApNum() == 0) ? 0 : 1;
	if (ql_nw_get_csq(ZQ_SIM_0, &cmd_pkg._4g_strength) != QL_NW_SUCCESS)
		cmd_pkg._4g_strength = 99;
	// calibrate m0 system time every 3600s
	ql_rtc_time_t local;
	ql_rtc_get_time(&local);
	static int cali_span = 1;	// 初始化为1，这样第一次会立刻校准
	if (local.tm_year > 2023 && zq_timer_s % cali_span == 0) {
		cmd_pkg.cali_time = ZqGetLocalStample();
		cali_span = 3600;
	} else {
		cmd_pkg.cali_time = 0;
	}

	ret = dtu_cmd02(&cmd_pkg, recv);
	return ret;
}

void iot_timer_cb(void* ctx) {
	zq_timer_s++;
}
void ZqDispatchEvent(void) {
	int ret;
	ql_event_t event;
	for (int i = 0; i < 10; i++) {		// handle up to 10 message at a time to prevent message blocking
		ret = ql_event_wait(&event, QL_NO_WAIT);
		if (ret != QL_OSI_SUCCESS) break;
		if (event.id == 0) continue; 	// 猜测这是定时器消息，定时器已经回调方式，没用。
		char* buf = (char*)event.param1;
		int len = (int)event.param2;
		if (buf == NULL || len == 0) continue;

		cJSON* root = cJSON_Parse((const char*)buf);
		if (root != NULL) {
			switch (event.id) {
			case ZQ_EVENT_MQTT_REQUEST:
				ZQLOG("Mqtt receive Event: ZQ_EVENT_MQTT_REQUEST, pkg len: %d", len);
				MqttInpubRequest(root);
				//mqtt_inpub_request(buf, len);
				break;
			case ZQ_EVENT_MQTT_REPLY:
				ZQLOG("Mqtt receive Event: ZQ_EVENT_MQTT_REPLY, pkg len: %d", len);
				MqttInpubReply(root);
				break;
			default:
				ZQLOG("@err@: Mqtt receive Event: unknown message");
				break;
			}
			cJSON_Delete(root);
		}
		free(buf);
	}
}

// 断网策略重连, 侦测到csq信号: 正常每1分钟连续尝试1次; 如果拨号成功，但是连不上服务器，5分钟尝试一次(减少流量损失)
static void IotOfflineReconnect(uint8_t csq, int offline_timer) {
	static int next_conn_time = 0;
	if (offline_timer == 0)	// 网络在线
		return;
	if (csq == 99 || csq <= 2) // 信号无效或者很弱
		return;
	int x = offline_timer - next_conn_time;
	if (x > 0) {// 已经过了重试时间, 希望立刻重试。但是测试经验，一般csq有效后2-5s后才能拨号成功，所以设置延迟5s(上电第一次除外)
		if (next_conn_time == 0) x = 0;
		else next_conn_time = (offline_timer + 5);
	}
	if (x == 0) { // 到达重试时间, 重试联网, 同时设置下次重试时间
		if (ql_datacall_get_sim_profile_is_active(ZQ_SIM_0, ZQ_PROFILE_IDX) == 1) // 拨号已经成功, 但是mqtt连接失败, 考虑到流量, 减少尝试频率
			next_conn_time = offline_timer + 5 * 60;	// 设置下次重试时间5分钟后
		else
			next_conn_time = offline_timer + 1 * 60;	// 设置下次重试时间1分钟后
		ZQLOG("Mqtt try to connect... ");
		ZqMqttConnect(IOT_MQTT_HOST, zq_imei, AKM_MQTT_KEY, zq_pms.devType);	 // try connect
		if (ZyIsMqttConnected()) 	// 连接成功, 尝试次数清0 - 设置最小尝试时间3*60s，防止网络时断时续(一连上就断)，导致流量消耗过快。
			next_conn_time = 3 * 60;
	}
}
// 断网策略重启, 10分钟 3次, 3小时 3次, 以后每24小时 1次
// 实测重启后, 仍然断网, 拨号会耗时2-3分钟。
static void IotOfflineReset(int offline_timer) {
	int offline_reset_stage = zq_local_saved.offline_reset_stage;
	if (offline_timer > 0) {
		// 判断各个阶段, 分别断网10分钟, 3小时,24小时重启, -5s是不干扰其它整点触发的事件.
		if ((offline_reset_stage >= 0 && offline_reset_stage < 3 && offline_timer >(10 * 60 - 5))
			|| (offline_reset_stage >= 3 && offline_reset_stage < 6 && offline_timer >(3 * 3600 - 5))
			|| (offline_reset_stage >= 6 && offline_timer > (24 * 3600 - 5))) {
			offline_reset_stage++;
		}
	} else if (offline_reset_stage != 0) { // offline == 0 && offline_reset_stage != 0 -- 联网清除重启标志
		offline_reset_stage = zq_local_saved.offline_reset_stage = 0;
		zq_EasyFileWrite(ZQ_CONFIG_FILE_PATH, &zq_local_saved, sizeof(zq_local_saved));
	}
	if (offline_reset_stage > zq_local_saved.offline_reset_stage) {
		ZQLOG("Offline timer overflow, stage = %d, reset...", offline_reset_stage);
		ql_rtos_task_sleep_ms(200); // 等待log输出
		zq_local_saved.offline_reset_stage = offline_reset_stage;
		zq_EasyFileWrite(ZQ_CONFIG_FILE_PATH, &zq_local_saved, sizeof(zq_local_saved));
		ZqStoreRtc();
		ql_power_reset(RESET_NORMAL);
		ql_rtos_task_sleep_s(60);
	}
}

void MemoryDbg(uint32_t sec) {
#ifndef ZQ_SIMULATOR
	if (sec % 30 == 0) {
		ql_memory_heap_state_t stack;
		ql_dev_memory_size_query(&stack);
		ZQLOG("memory avail_size: %d, total_size: %d, max_block_size: %d", stack.avail_size, stack.total_size, stack.max_block_size);
	}
#endif
}
// will be called every 1s
static void IotMainLoop(void) {
	static uint32_t last_timer = 0;
	static int start_timer = -1;
	static int online_state = 0;
	static int online_timer = 0;
	static int offline_timer = 0;

	offline_timer = last_timer = zq_timer_s;  // 认为上电初始是断网状态, 记录初始offline时间, 因为如果上电就断网, 很可能拨号会卡2-3分钟。
	for (;; ql_rtos_task_sleep_ms(100)) {
		int ret = 0;
		ZqDispatchEvent(); // 处理消息

		// 以下是秒级处理流程 -----------------------------------------------------------
		if (zq_timer_s == last_timer) continue;
		last_timer = zq_timer_s;
		MemoryDbg(zq_timer_s); // 内存测试函数, 30s一次

		// 如果上一轮开始断电, 等待cat1处理完上报和OTA，通知mcu可以关闭。(online_state >= 8 即如果刚上电: 要等待第一次上报发送出去才能动作)
		if (zq_pms.outageTemp == 1 && online_state >= 8 && ZqOtaGetState() == 0) { // if (zq_pms.outageTemp == 1 && online_state >= 8) {
			dtu_cmd75();
			return;
		}

		// 1s 定时采样定位数据
		GetBtsInfo(zq_pms.btsInfo, ZQ_BASE_SCAN_BUFLEN);
		zq_pms.nearsbtsNum = GetNearBtsInfo(zq_pms.nearsbtsInfo, ZQ_BASE_SCAN_BUFLEN * ZQ_BASE_SCAN_NEAR_CNT);
		zq_pms.apNum = zq_GetApInfo(zq_pms.apInfo, ZQ_AP_INFO_BUFLEN);
		uint8_t csq = GetSignalIntensity();
		sprintf(zq_pms.signalIntensity, "%d", csq);
		// 1s心跳和抓取mcu数据
		char* updates[14], * alarms[4];
		int update_cnt = 0, alarm_cnt = 0;
		int old_localAction = zq_pms.localAction;
		if (CFG_DEVICE_TYPE != 906)	// akm906 has not MCU
		{
			DtuC02ResponsePkg recv;
			ret = IotQueryMcuData(&recv); // 读取mcu数据
			if (ret == ZQ_RET_OK)
				IotMergeDtuData(&recv, updates, &update_cnt, alarms, &alarm_cnt);
		}

		// 处理断网重连策略
		IotOfflineReconnect(csq, offline_timer);
		// 处理断网重启策略
		IotOfflineReset(offline_timer);
		// 联网状态侦测
		if (ZyIsMqttConnected()) {
			if (offline_timer > 0) {
				online_state = 1;
				offline_timer = 0;
			}
			online_timer++;
		} else {
			online_state = online_timer = 0;
			offline_timer++;
		}

		// 重新连接平台成功, 一些初始化动作, 考虑到任务轮询时间间隔, 分几次逐步发送。
		switch (online_state) {
		case 0:
			if (start_timer < 0) { // 初次上电断网
				if (zq_timer_s > 60) { // 进入主循环60s(大概上电3分钟了: 上电拨号两分钟+此处60s)以上依然没有联网成功，开始offline report
					ZQLOG("0. Offline > 60s, will begin first offline Mqtt Update report - MqttPublishUpReport");
					ret = MqttPublishUpReport();
					start_timer = 0; // 开始report周期计时
				}
			} else // 正在在线的情况下断网，直接走离线报告
				online_state = 9;
			break;
		case 1: // 订阅
			ZQLOG("1. Online, Mqtt Register Device - MqttPublishUpInfo");
			ret = ZqMqttRegistTopic(ZQ_EVENT_MQTT_REQUEST, iot_topic_request);
			ret |= ZqMqttRegistTopic(ZQ_EVENT_MQTT_REPLY, iot_topic_reply);
			ZQ_ASSERT((ret == ZQ_RET_OK), "Iot subscription fail!!"); // 失败600s后重启
			online_state++;
			continue;
		case 2: // 进行注册，发布设备基础信息（3.3）
			ZQLOG("2. Online, Mqtt Register Device - MqttPublishUpInfo");
			ret = MqttPublishUpInfo();
			ZQ_ASSERT((ret == ZQ_RET_OK), "Iot register fail!!"); // 失败600s后重启
			online_state++;
			continue;
		case 3: // 请求同步服务器时间
			ZQLOG("3. Online, Mqtt Quest Time - MqttPublishQuestTimestamp");
			ret = MqttPublishQuestTimestamp(); // 请求同步服务器时间
			online_state++;
			continue;
		case 4: // 客户要求: query 应该在注册后收到平台的查询命令第一时间根据目前模块的参数回复平台查询的参数，应该在 shadow 之前， info 之后, 确保 query 上报的数据是在根据 shadow 更改前的数据。
			// 等待query回复标志，或者30s超时
			ZQLOG("4. Online, waiting for first query, timer :%d", online_timer);
			if (zq_first_query || online_timer > 60) online_state++;
			continue;
		case 5: // 影子信息获取，根据版本及属性更新（3.8 3.9 3.10）
			ZQLOG("5. Online, Mqtt Quest Shadow - MqttPublishQuestShadow");
			ret = MqttPublishQuestShadow();
			online_state++;
			continue;
		case 6: // 所有告警/告警解除上报（3.5）
			ZQLOG("6. Online, Mqtt Alarm sync - MqttPublishUpAlarm");
			ret = MqttPublishUpAlarm((char**)zq_mqtt_up_alarm_strs, zq_mqtt_up_alarm_num);
			online_state++;
			continue;
		case 7: // 设备数据上报（3.4）-- 重连后会立刻增加一次上报，不影响周期上报执行（不然问题多多，不太好处理）
			if ((zq_pms.apNum > 0 && zq_pms.nearsbtsNum > 0) || zq_timer_s > 60) { // wifi for scan OK, or overtime
				ZQLOG("6. Online, Mqtt Update report - MqttPublishUpReport");
				ret = MqttPublishUpReport();
				online_state++;
				start_timer = (start_timer < 0) ? 0 : zq_timer_s; // 开始report周期计时
			}
			continue;
		case 8: // 上线，尝试发送所有offline数据, 客人要求尽量一次发送最大数200条。
			if(MqttPublishUpReportOffline() == 0) online_state++;
			continue;
		}

		// 处理报警和更新
		if (online_state >= 9) {
			// 是否有alarm处理
			if (alarm_cnt > 0) {
				ZQLOG("Mqtt Alarm - MqttPublishUpAlarm");
				ret = MqttPublishUpAlarm(alarms, alarm_cnt);
			}
			// 是否有update处理
			if (update_cnt > 0) { // TODO, aucma上电不需要更新
				ZQLOG("Mqtt Update - MqttPublishUpUpdate");
				ret = MqttPublishUpUpdate(updates, update_cnt);
			}
			// 断电处理, 上报一次信息
			if (zq_pms.outageTemp == 1) {
				MqttPublishUpReport();
				// 等下一个周期会发cmd75通知mcu可以进低功耗
				continue;
			}
			// 本地关机处理, 上报一次信息
			if (zq_pms.localAction == 0 && old_localAction == 1) {
				MqttPublishUpReport();
				continue;
			}

			// 除上电第一次以外, 的周期上报
			int interval = (zq_timer_s <= 40 * 60) ? 10 * 60 : zq_pms.uploadInterval * 60; // 前40分钟, 每10分钟上报
			if ((zq_timer_s - start_timer) >= interval) {
				start_timer = zq_timer_s;
				MqttPublishUpReport();
				continue;
			}
		}
	}
}

static void IotConfigRtc(void) {
	ql_rtc_cfg_t cfg;
	ql_rtc_get_cfg(&cfg);
	cfg.nwt_cfg = 0;
	cfg.tz_cfg = 0;
	ql_rtc_set_cfg(&cfg);
	return;
}

static void zq_iot_thread(void* arg) {
	int ret = 0;
	// 1. config rtc time to sync with base station. 
	// 修改1: 实际使用有个别基站不能同步时间(可能会同步到2000年)，改用NTP同步。关闭此功能是防止断网重连后同步到基站时间
	// 修改2:  断网重启尽量保持时间正确。
	ZQLOG("1. config rtc auto sync with base station");
	IotConfigRtc();
	ZqLoadRtc();
	// 2. 启动1s通用定时器
	ZQLOG("2. Start 1s general timer. cycle forever.");
	ret |= ql_rtos_timer_create(&zq_iot_timer, NULL, iot_timer_cb, NULL);
	ret |= ql_rtos_timer_start(zq_iot_timer, 1000, 1);
	MemoryDbg(0); 	// 内存测试函数, 30s一次
	ZQ_ASSERT((ret == ZQ_RET_OK), "Iot start timer fail!!");
	// 3. init dtu function
	ZQLOG("3. init dtu function ...");
	zq_dtu_start();
	// 4. init all iot parameters
	ZQLOG("4. init all iot parameters");
	ZqInitParams();
	// 5. Start base scan function
	ZQLOG("5. Start base scan function...");
	btsinfo_init();
	// 6. Start wifi scan function
	ZQLOG("6. Start wifi scan function...");
	ql_wifiscan_init();
	// 7. Start data call - 测试经验，一般上电4-5秒后csq有效, csq有效后2-5s后才能拨号成功(总计上电后6-10s才能成功), 所以尽量靠后启动
	// kg modify 20231030, 上电首次拨号，等待注网成功
	ZQLOG("7.1. wait for network register..., overtime = 120s...");
	ql_network_register_wait(ZQ_SIM_0, 120);
	ZQLOG("7.2. Start data call...");
	for (int i = 0; i < 30; i++, ql_rtos_task_sleep_s(1)) {
		uint8_t csq = 99;
		ql_nw_get_csq(ZQ_SIM_0, &csq);
		ZQLOG("try data call: %d, csq: %d", i + 1, csq);
		if (csq == 99 || csq <= 2) continue;// 信号无效或者很弱
		if (zq_StartDataCall() == ZQ_RET_OK) break;
	}

	// 8. Start NTP App
	ZQLOG("8. Start ntp app...");
	zq_ntp_init();
	// 9. Start fota function
	ZQLOG("9. Start fota function...");
	zq_fota_init();

	// 10. Init mqtt 
	ZQLOG("10. Start mqtt function...");
	// init and build Topic string
	InitTopicString();
	// init mqtt protocol 
	zq_MqttInit(zq_iot_task);
	// 11. 等待RTC校准成功
	for (int i = 0; i < 30; i++, ql_rtos_task_sleep_s(1)) {  // retry 30 times, 30s
		ZQLOG("11. Start RTC calibration and wait for OTA detection completed...");
		ql_rtc_time_t local;
		ql_rtc_get_time(&local);
		if (local.tm_year >= 2023) break;
	}
	// 12. start main loop
	ZQLOG("12. Start main loop...");
	IotMainLoop();
}

//  -----------------------------------------------------------------------------------------------
void zq_app_init(void) {
	ZQLOG("App starting...ver = %s", ZQ_SW_VER_STR);

#ifdef ZQ_DEBUG
	ql_rtos_task_sleep_ms(5000); // TODO: 延迟5s，防止死机烧录程序困难
	ZQLOG("AKM APP end debug delay......");
#endif // ZQ_DEBUG

#if (!ZQ_SIMULATOR)
	// [panzhimin] ftm start
	{
		extern bool ftm_start(void);
		if (!ftm_start()) {
			int ret = ql_rtos_task_create(&zq_iot_task, 40 * 1024, APP_PRIORITY_NORMAL, "ITC_ZY_APP", zq_iot_thread, NULL, 10);
			ZQ_ASSERT((ret == ZQ_RET_OK), "APP Init fail!!");
		}
	}
	// [panzhimin] ftm end
#else
	int ret = ql_rtos_task_create(&zq_iot_task, 40 * 1024, APP_PRIORITY_NORMAL, "ITC_ZY_APP", zq_iot_thread, NULL, 10);
	ZQ_ASSERT((ret == ZQ_RET_OK), "APP Init fail!!");
#endif // ZQ_SIMULATOR
}

// 软件版本定义以及改动点记录 ----------------------------------------------------------------------------------
// 软件版本定义 ---------------------------------------
// set a fixed string in image, like "***AUTO_VER_M0:3001001036++AAKM_V1.0.2.00", 供自动化版本工具使用, 要求:
// 1. "***AUTO_VER:" - 开头固定特征串, 长度12
// 2. "AKM_BLE_NAME+" - 蓝牙名称, 长度12, 不足的用"+"填充, 至少1个"+"
// 3. "ZQ_VER_PREFIX"H_V"ZQ_M0_HW_VER" - 版本前缀加"M_V"加版本号, 版本号从1.0.0.00开始, 固定格式a.b.c.de
const char* ZQ_AUTO_SW_VER = "***AUTO_VER:"AKM_BLE_NAME""AKM_BLE_NAME_SUFFIX"+"ZQ_VER_PREFIX"S_V""1.0.0.00";
const char* ZQ_AUTO_HW_VER = ZQ_VER_PREFIX"H_V""1.0.0.00";

// 版本修改记录 ---------------------------------------
/*
## Cat1版本修改记录:
V1.0.0: -- 开始HW测试版本

*/

// 版本演进计划 ---------------------------------------
/*
*/
